/**
* \file DGVPlot.cpp
*
* \brief This file defines the class for doing 1D plots using only Qt and QWT.
* \author Shekhar S. Chandra, 2008
*
* This file is part of DGV Library.
*
* DGV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DGV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DGV.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <qwt_plot_canvas.h> //Plot Canvas for saving
#include <qwt_legend.h>
#include <qwt_scale_div.h>
#include <QApplication>
#include <QFileDialog>
#include <QInputDialog>
#include <QMenu>
#include <QContextMenuEvent>
#include <QtSvg/QSvgGenerator>
#include <QPainter>
#include <QPrinter>

#include "DGVPlot.h"
#include "DGVFile.h"

DGVPlot::DGVPlot(QWidget *parent) : QwtPlot(parent)
{
    loaded = false;
    linePlot = true;

    //Set background
    const QColor bgColor(Qt::white);
    QPalette p = palette();
    p.setColor(backgroundRole(), bgColor);
    setPalette(p);

    antialiased = false;
    plotSize = 0;
}

DGVPlot::~DGVPlot()
{

}

void DGVPlot::setName(QString filename)
{
    name = filename;
    setWindowTitle(strippedNamePrefix());

    emit nameChanged(strippedNamePrefix());
}

inline void DGVPlot::setToLinePlot(bool lines)
{
    linePlot = lines;
}

void DGVPlot::setData(Array<imageType,1> &xData, Array<imageType,1> &yData)
{
    xArray.resize(1);
    yArray.resize(1);

    plotSize = xData.size();
    xArray(0).resize(xData.shape());
    yArray(0).resize(yData.shape());
    xArray(0) = xData;
    yArray(0) = yData;
    loaded = true;
}

void DGVPlot::setData(Array< Array<imageType,1>, 1 > &xData, Array< Array<imageType,1>, 1 > &yData)
{
    xArray.resize(xData.shape());
    yArray.resize(yData.shape());

    for(int j = 0; j < xArray.size(); j ++)
    {
        plotSize = xArray(j).size();
        xArray(j).resize(xData(j).shape());
        yArray(j).resize(yData(j).shape());
        xArray(j) = xData(j);
        yArray(j) = yData(j);
    }
    loaded = true;
}

void DGVPlot::createCurvePlots(Array< Array<imageType,1>, 1 > &xData, Array< Array<imageType,1>, 1 > &yData, QString plotTitle)
{
    QString curveName;
    Qt::GlobalColor colourCount;
    QwtPlotCurve *tmpCurve = NULL;

    curves.resize(xData.size());

    QwtPlot::setTitle(plotTitle);
    QwtPlot::insertLegend(new QwtLegend(), QwtPlot::RightLegend);
    for(int j = 0; j < xData.size(); j ++)
    {
        curveName = "Sel " + curveName.setNum(j);
        //cerr << "Curve: " << curveName.toStdString() << endl;
        tmpCurve = curves(j) = new QwtPlotCurve(curveName);
        if(antialiased)
            tmpCurve->setRenderHint(QwtPlotItem::RenderAntialiased);
        if(linePlot)
            tmpCurve->setStyle(QwtPlotCurve::Lines);
        else
            tmpCurve->setStyle(QwtPlotCurve::Dots);
        colourCount = static_cast<Qt::GlobalColor>((7+j)%20); //Start colour from red
        tmpCurve->setPen(QColor(colourCount));
        tmpCurve->setData(&(xData(j)(0)), &(yData(j)(0)), xData(j).size());
        tmpCurve->attach(this);
    }
    QwtPlot::setAutoReplot(true);
    QwtPlot::replot();
}

void DGVPlot::createCurvePlot(Array<imageType,1> xData, Array<imageType,1> yData, QString plotTitle)
{
    Qt::GlobalColor colourCount;
    QwtPlotCurve *tmpCurve = NULL;

    curves.resize(1);

    QwtPlot::setTitle(plotTitle);
    QwtPlot::setWindowTitle(plotTitle);

    tmpCurve = curves(0) = new QwtPlotCurve;
    if(antialiased)
        tmpCurve->setRenderHint(QwtPlotItem::RenderAntialiased);
    colourCount = static_cast<Qt::GlobalColor>(7); //Start colour from red
    if(linePlot)
        tmpCurve->setStyle(QwtPlotCurve::Lines);
    else
        tmpCurve->setStyle(QwtPlotCurve::Dots);
    tmpCurve->setPen(QColor(colourCount));
    tmpCurve->setData(&(xData(0)), &(yData(0)), xData.size());
    tmpCurve->attach(this);

    QwtPlot::setAutoReplot(true);
    QwtPlot::replot();
}

void DGVPlot::generatePlot()
{
    if(loaded)
    {
        createCurvePlots(xArray, yArray, name);
    }
}

bool DGVPlot::loadGNU(QString filename)
{
    DGVFile inFile;
    Array<imageType,1> tuple;
    bool ok;

    QString sep = QInputDialog::getText(this, tr("Values Separator"), tr("Separator (a space normally): "), QLineEdit::Normal,
                                          " ", &ok);

    if(ok)
    {
        setName(filename);
        inFile.setSeparator(sep);
        inFile.setCommentSymbol("#");

        if(!inFile.open(filename,"r"))
            return false;
        if(inFile.atEnd())
            return false;

        inFile.readNextLineAsDoubles(tuple);
        while(tuple.size() == 0) //Ignore comments
            inFile.readNextLineAsDoubles(tuple);

        if(tuple.size() == 2)
        {
            xArray.resize(1);
            yArray.resize(1);
            plotSize = 1;

            xArray(0).resizeAndPreserve(plotSize);
            xArray(0)(plotSize-1) = tuple(0);
            yArray(0).resizeAndPreserve(plotSize);
            yArray(0)(plotSize-1) = tuple(1);
            while(!inFile.atEnd())
            {
                qApp->processEvents();

                inFile.readNextLineAsDoubles(tuple);
                while(tuple.size() == 0 && !inFile.atEnd()) //Ignore comments
                    inFile.readNextLineAsDoubles(tuple);

                plotSize ++;
                xArray(0).resizeAndPreserve(plotSize);
                xArray(0)(plotSize-1) = tuple(0);
                yArray(0).resizeAndPreserve(plotSize);
                yArray(0)(plotSize-1) = tuple(1);
            }

            cerr << "Load GNU Size: " << xArray(0).size() << endl;
            createCurvePlots(xArray, yArray, filename);
            inFile.close();
            loaded = true;
        }
        else
        {
            cerr << "Number of Tuples: " << tuple.size() << " is not supported." << endl;
            return false;
        }
    }
    else
        return false;

    return true;
}

bool DGVPlot::saveGNU(QString filename)
{
    if(loaded)
    {
        DGVFile outFile;
        Array<imageType,1> tuple(2);
        QString tmpNum1, tmpNum2;
        bool ok;

        QString sep = QInputDialog::getText(this, tr("Values Separator"), tr("Separator (a space normally): "), QLineEdit::Normal,
                                          " ", &ok);

        if(ok)
        {
            outFile.setSeparator(sep);
            outFile.setCommentSymbol("#");
            if(!outFile.open(filename,"w"))
                return false;

            //outFile.writeComment("Written by the Discrete Geometry Viewer");
            for(int j = 0; j < xArray.size(); j ++)
            {
                cerr << "Save GNU Size: " << xArray(j).size() << endl;
                for(int k = 0; k < xArray(j).size(); k ++)
                {
                    tuple(0) = xArray(j)(k);
                    tuple(1) = yArray(j)(k);
                    outFile.writeDoubleArrayAsLine(tuple);
                }
            }

            outFile.close();
            return true;
        }
        else
            return false;
    }
    else
        return false;
}

//Slots
void DGVPlot::save()
{
    QPixmap image;
    //QImage tmp;
    QFileDialog *fileSaver = new QFileDialog(this);
    QString filename = fileSaver->getSaveFileName(this,
                           tr("Select File Name to Save"),
                           ".",
                           tr("Images (*.png *.ppm *.jpeg *.jpg *.bmp);;GNUPlot 2D Files (*.dat *.gnu *.gnuplot);;Vector Graphics (*.svg *.ps *.pdf)"));

    if (!filename.isEmpty())
    {
        QFileInfo fi(filename);
        QString extension = fi.suffix().toLower();

        if(extension == "dat" || extension == "gnu" || extension == "gnuplot")
            saveGNU(filename);
        else if(extension == "svg")
        {
            QSvgGenerator svg;
            svg.setFileName(filename);
            svg.setSize(rect().size());

            QPainter painter(&svg);
            print(&painter, rect());
            painter.end();
        }
        else if(extension == "ps")
        {
            QPrinter printer(QPrinter::HighResolution);
            printer.setOutputFormat(QPrinter::PostScriptFormat);
            printer.setOrientation(QPrinter::Landscape);
            printer.setOutputFileName(filename);
            printer.setCreator("DGV");

            print(printer);
        }
        else if(extension == "pdf")
        {
            QPrinter printer(QPrinter::HighResolution);
            printer.setOutputFormat(QPrinter::PdfFormat);
            printer.setOrientation(QPrinter::Landscape);
            printer.setOutputFileName(filename);
            printer.setCreator("DGV");

            print(printer);
        }
        else
        {
            ///\todo Bug: Saves plots in monochrome only. Works ok in Linux though.
            //tmp.setNumColors(INT_MAX);
            //tmp.convertToFormat(QImage::Format_RGB32,Qt::AutoColor);
            //image.fromImage(tmp,Qt::AutoColor);
            //cerr << "Colours: " << image.numColors() << endl;
            image = QPixmap::grabWidget(this);
            //image = QPixmap::grabWindow(this->winId());
            image.save(filename,0,100);
        }
    }
}

void DGVPlot::xLabel()
{
    bool ok;
    QString text = QInputDialog::getText(this, tr("x-axis Label"),
                                          tr("New Label:"), QLineEdit::Normal,
                                          "x", &ok);
    if(ok)
        setAxisTitle(xBottom,text);
}

void DGVPlot::yLabel()
{
    bool ok;
    QString text = QInputDialog::getText(this, tr("y-axis Label"),
                                          tr("New Label:"), QLineEdit::Normal,
                                          "y", &ok);
    if(ok)
        setAxisTitle(yLeft,text);
}

void DGVPlot::titleLabel()
{
    bool ok;
    QString text = QInputDialog::getText(this, tr("Title Label"),
                                          tr("New Label:"), QLineEdit::Normal,
                                          "z", &ok);
    if(ok)
        setTitle(text);
}

void DGVPlot::renameData()
{
    bool ok;

    QString newName = QInputDialog::getText(this, tr("Rename Data"), tr("New Name: "), QLineEdit::Normal, name, &ok);

    if(ok)
        setName(newName);
}

void DGVPlot::rescaleAxis(const int axis)
{
    bool ok;
#if QWT_VERSION < 0x050200

    double scaleMin = QInputDialog::getDouble(this, tr("Rescale"),
                                          tr("New Min:"), axisScaleDiv(axis)->lBound(),
                                          -2147483647, 2147483647, 1, &ok);
    double scaleMax = QInputDialog::getDouble(this, tr("Rescale"),
                                          tr("New Max:"), axisScaleDiv(axis)->hBound(),
                                          -2147483647, 2147483647, 1, &ok);
#else
    double scaleMin = QInputDialog::getDouble(this, tr("Rescale"),
                                          tr("New Min:"), axisScaleDiv(axis)->lowerBound(),
                                          -2147483647, 2147483647, 1, &ok);
    double scaleMax = QInputDialog::getDouble(this, tr("Rescale"),
                                          tr("New Max:"), axisScaleDiv(axis)->upperBound(),
                                          -2147483647, 2147483647, 1, &ok);
#endif

    if(ok)
        setAxisScale(axis, scaleMin, scaleMax);
}

void DGVPlot::rescaleXAxis()
{
    rescaleAxis(2);
}

void DGVPlot::rescaleYAxis()
{
    rescaleAxis(0);
}

void DGVPlot::toggleLinePlot()
{
    if(linePlotAct->isChecked())
        linePlot = true;
    else
        linePlot = false;

    if(linePlot)
    {
        for(int j = 0; j < curves.size(); j ++)
            curves(j)->setStyle(QwtPlotCurve::Lines);
    }
    else
    {
        for(int j = 0; j < curves.size(); j ++)
            curves(j)->setStyle(QwtPlotCurve::Dots);
    }
}

QString DGVPlot::strippedName()
{
    return QFileInfo(name).fileName();
}

QString DGVPlot::strippedNamePrefix()
{
    return "Plot: " + QFileInfo(name).fileName();
}

void DGVPlot::contextMenuEvent(QContextMenuEvent *event)
{
    QMenu menu(this);

    ///Setup Save action
    saveAct = menu.addAction(tr("&Save"));
    saveAct->setShortcut(tr("Ctrl+s"));
    connect(saveAct, SIGNAL(triggered()), this, SLOT(save()));
    menu.addSeparator()->setText("Labels");
    ///Labels
    xLabelAct = menu.addAction(tr("Set &x Label"));
    xLabelAct->setShortcut(tr("Ctrl+x"));
    connect(xLabelAct, SIGNAL(triggered()), this, SLOT(xLabel()));
    yLabelAct = menu.addAction(tr("Set &y Label"));
    yLabelAct->setShortcut(tr("Ctrl+y"));
    connect(yLabelAct, SIGNAL(triggered()), this, SLOT(yLabel()));
    titleAct = menu.addAction(tr("Set &Title"));
    titleAct->setShortcut(tr("Ctrl+t"));
    connect(titleAct, SIGNAL(triggered()), this, SLOT(titleLabel()));
    xScaleAct = menu.addAction(tr("Rescale x-axis"));
    xScaleAct->setShortcut(tr("Ctrl+a"));
    connect(xScaleAct, SIGNAL(triggered()), this, SLOT(rescaleXAxis()));
    yScaleAct = menu.addAction(tr("Rescale y-axis"));
    yScaleAct->setShortcut(tr("Ctrl+b"));
    connect(yScaleAct, SIGNAL(triggered()), this, SLOT(rescaleYAxis()));
    linePlotAct = menu.addAction(tr("Line Plot"));
    linePlotAct->setCheckable(true);
    linePlotAct->setChecked(linePlot);
    linePlotAct->setShortcut(tr("Ctrl+p"));
    connect(linePlotAct, SIGNAL(triggered()), this, SLOT(toggleLinePlot()));
    menu.addSeparator();
    ///Setup Close action
    renameAct = menu.addAction(tr("Rename"));
	renameAct->setShortcut(tr("Ctrl+r"));
	connect(renameAct, SIGNAL(triggered()), this, SLOT(renameData()));
	refreshAct = menu.addAction(tr("Refresh"));
	refreshAct->setShortcut(tr("F5"));
	connect(refreshAct, SIGNAL(triggered()), this, SLOT(replot()));
    closeAct = menu.addAction(tr("&Close"));
    closeAct->setShortcut(tr("Ctrl+c"));
    connect(closeAct, SIGNAL(triggered()), this, SLOT(close()));

    menu.exec(event->globalPos());
}
